<?php
class Bliss_User
{
	/**
	 * Holds the instance of this class
	 *
	 * @var	Bliss_User
	 */
	protected static $_instance;

	/**
	 * User's database record
	 *
	 * @var	array
	 */
	protected static $_data = array();

	/**
	 * Authentication instance
	 *
	 * @var	Zend_Auth
	 */
	protected $_auth;

	/**
	 * Database authentication adapter
	 *
	 * @var	Zend_Auth_Adapter_DbTable
	 */
	protected $_authAdapter;

	/**
	 * Stores any authentication errors
	 *
	 * @var	string
	 */
	protected $_authError;

	/**
	 * Stores loaded plugin instances
	 *
	 * @var array
	 */
	protected $_plugins = array();

	/**
	 * The ACL object
	 *
	 * @var Zend_Acl
	 */
	protected $_acl;

	/**
	 * Protected contructor that can only be called within the class
	 * using the getInstance method
	 */
	protected function __construct() 
	{
		$this->_auth = Zend_Auth::getInstance();
		$this->_authAdapter	= new Zend_Auth_Adapter_DbTable(
			Zend_Registry::get('db'),
			'users',
			'email',
			'password',
			"MD5(CONCAT('". Zend_Registry::get('authSalt') ."', ?))"
		);
	}

	/**
	 * Used to easily call plugins
	 *
	 * @param string $name
	 * @param array $arguments
	 */
	public function  __call($name, $arguments)
	{
		$class	= "Bliss_User_Plugin_{$name}";
		if(isset($this->_plugins[$class])) {
			$plugin	= $this->_plugins[$name];

		} elseif(class_exists($class)) {
			$plugin	= new $class($this);
		} else {
			return false;
		}

		if(method_exists($plugin, $name)) {
			return call_user_func_array(array($plugin, $name), $arguments);
		}

		return false;
	}

	/**
	 * Returns the instance of the class.
	 * If the class has not been instantiated, it does so
	 *
	 * @return	Bliss_User
	 */
	public function getInstance() 
	{
		if(!isset(self::$_instance)) {
			self::$_instance = new self();
		}

		return self::$_instance;
	}

	/**
	 * Sets the storage class to use
	 *
	 * @param	mixed
	 */
	public function setStorage($storage)
	{
		$this->_auth->setStorage($storage);
	}
	
	/**
	 * Checks if the user has an identity stored
	 *
	 * @return	bool
	 */
	public function hasIdentity() 
	{
		return $this->_auth->hasIdentity() && ($this->getIdentity() !== false);
	}

	/**
	 * Stores the user's identity
	 *
	 * @param string $session_id
	 */
	protected function storeIdentity($session_id)
	{
		$storage = $this->_auth->getStorage();
		$storage->write($session_id);
	}
	
	/**
	 * Gets the identity of the user
	 *
	 * @return	array	User's data
	 */
	public function getIdentity()
	{
		if(count(self::$_data)) return self::$_data;

		$id		= $this->_auth->getIdentity();
		$db		= Zend_Registry::get('db');
		$query	= $db->select()->from('users')->where('session_id=?', $id);
		$result = $db->fetchRow($query);
		
		if($result) {
			unset($result['password']);
			self::$_data = (object)$result;
			return self::$_data;
		}

		return false;
	}

	/**
	 * Attempts to login using the credentials provided.
	 *
	 * @param string $email
	 * @param string $password
	 * @return bool
	 */
	public function login($email, $password) 
	{
		$this->_authAdapter->setIdentity($email);
		$this->_authAdapter->setCredential($password);
		$result	= $this->_auth->authenticate($this->_authAdapter);

		if($result->isValid()) {
			$session_id = md5(uniqid(null, true));
			Zend_Registry::get('db')->query("UPDATE `users` SET `session_id` = '{$session_id}' WHERE `email` = '{$email}'");

			self::$_data = $this->_authAdapter->getResultRowObject(null, 'password');
			$this->storeIdentity($session_id);
			return true;

		} else {
			$this->_authError = $result->getCode();
			return false;
		}
	}

	/**
	 * Logs the user out and clears their identity
	 */
	public function logout() 
	{
		$this->_auth->clearIdentity();
	}

	/**
	 * Gets a single field from the user's identity
	 */
	public function __get($key) 
	{
		$this->getIdentity();
		
		return isset(self::$_data->{$key}) ? self::$_data->{$key} : null;
	}

	/**
	 * Sets the ACL
	 *
	 * @param Zend_Acl $acl
	 */
	public function setAcl(Zend_Acl $acl)
	{
		$this->_acl	= $acl;
	}

	public function getAcl()
	{
		if(!isset($this->_acl)) {
			throw new Exception("No ACL has been set for the user object.");
		}
		return $this->_acl;
	}
}
?>
